package com.peas.platform.module.icims.budget.apply.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import com.peas.platform.common.core.enums.BaseEnum;
import com.peas.platform.framework.mybatisplus.domain.BaseDomain;
import com.peas.platform.framework.mybatisplus.po.BasePo;
import com.peas.platform.framework.mybatisplus.service.BaseServiceImpl;
import com.peas.platform.module.icims.base.approve.component.ApproveCallback;
import com.peas.platform.module.icims.base.approve.dto.ApproveDto;
import com.peas.platform.module.icims.base.approve.dto.ApproveOrderNodeDto;
import com.peas.platform.module.icims.base.approve.enums.ApproveOrderBusinessType;
import com.peas.platform.module.icims.base.approve.service.ApproveOrderService;
import com.peas.platform.module.icims.base.approve.service.ApproveService;
import com.peas.platform.module.icims.base.mdm.dto.GovernmentDto;
import com.peas.platform.module.icims.base.mdm.service.GovernmentService;
import com.peas.platform.module.icims.budget.apply.api.req.SaveBudgetOneVo;
import com.peas.platform.module.icims.budget.apply.api.req.SubmitBudgetOneVo;
import com.peas.platform.module.icims.budget.apply.controller.vo.*;
import com.peas.platform.module.icims.budget.apply.domain.BudgetOneDomain;
import com.peas.platform.module.icims.budget.apply.dto.*;
import com.peas.platform.module.icims.budget.apply.enums.BudgetState;
import com.peas.platform.module.icims.budget.apply.service.*;
import com.peas.platform.module.icims.budget.archives.dto.BudgetKpiDto;
import com.peas.platform.module.icims.budget.archives.enums.KpiSource;
import com.peas.platform.module.icims.budget.archives.service.BudgetKpiService;
import com.peas.platform.module.icims.budget.project.dto.BudgetProjectTwoDto;
import com.peas.platform.module.icims.budget.project.enums.BudgetProjectTwoLibraryState;
import com.peas.platform.module.icims.budget.project.enums.BudgetProjectTwoState;
import com.peas.platform.module.icims.budget.project.service.BudgetProjectTwoService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;

@Service
public class BudgetOneServiceImpl extends BaseServiceImpl<BudgetOneDto> implements BudgetOneService, ApproveCallback {

    @Resource
    private BudgetOneDomain budgetOneDomain;
    @Resource
    private BudgetProjectTwoService budgetProjectTwoService;
    @Resource
    private BudgetOnePurchaseService budgetOnePurchaseService;
    @Resource
    private BudgetOneItemService budgetOneItemService;
    @Resource
    private BudgetOneYearService budgetOneYearService;
    @Resource
    private BudgetOneKpiService budgetOneKpiService;
    @Resource
    private BudgetOneFileService budgetOneFileService;
    @Resource
    private ApproveService approveService;
    @Resource
    private ApproveOrderService approveOrderService;
    @Resource
    private GovernmentService governmentService;
    @Resource
    private BudgetKpiService budgetKpiService;

    @Override
    public BaseDomain<BudgetOneDto, ? extends BasePo> getDomain() {
        return budgetOneDomain;
    }

    private BudgetOneDto getOneByProjectTwoId(Long projectTwoId) {
        Assert.notNull(projectTwoId, "二级项目ID不能为空");
        BudgetOneDto c = new BudgetOneDto();
        c.setProjectTwoId(projectTwoId);
        return this.getOne(c, false);
    }

    @Transactional
    @Override
    public BudgetOneDto saveBudgetOne(SaveBudgetOneVo vo) {
        BudgetOneDto bod = BeanUtil.copyProperties(vo, BudgetOneDto.class);
        if (StrUtil.isBlank(bod.getState())) {
            bod.setState(BudgetState.save.getKey());
        }
        Assert.notNull(vo.getProjectTwoId(), "请选择二级项目");
        BudgetProjectTwoDto projectTwoDto = budgetProjectTwoService.getById(vo.getProjectTwoId());
        Assert.notNull(projectTwoDto, "二级项目不存在");
        vo.setApplyOneFund(null);
        vo.setApproveOneFund(null);
        int result;
        if (vo.getId() != null) {
            BudgetOneDto bean = this.getById(vo.getId());
            BudgetState state = BaseEnum.key2Enum(BudgetState.class, bean.getState());
            Assert.isTrue(BudgetState.save == state || BudgetState.revocation == state || BudgetState.reject == state, "一上预算编制[" + bean.getCode() + "]的状态为[" + state.getKey() + "-" + state.getValue() + "]，不允许修改");
            result = this.updateById(bod);
        } else {
            BudgetOneDto bean = this.getOneByProjectTwoId(vo.getProjectTwoId());
            if (bean != null) {
                BudgetState state = BaseEnum.key2Enum(BudgetState.class, bean.getState());
                Assert.isTrue(BudgetState.save == state || BudgetState.revocation == state || BudgetState.reject == state, "一上预算编制[" + bean.getCode() + "]的状态为[" + state.getKey() + "-" + state.getValue() + "]，不允许修改");
                bod.setId(bean.getId());
                bod.setVersion(bean.getVersion());
                result = this.updateById(bod);
            } else {
                result = this.save(bod);
            }
        }
        Assert.isTrue(result > 0, "保存失败");
        List<BudgetOnePurchaseDto> bopds = budgetOnePurchaseService.listByField("budgetOneId", bod.getId());
        budgetOnePurchaseService.removeBatchByList(bopds);
        List<BudgetOneItemDto> boids = budgetOneItemService.listByField("budgetOneId", bod.getId());
        budgetOneItemService.removeBatchByList(boids);
        List<BudgetOneYearDto> boyds = budgetOneYearService.listByField("budgetOneId", bod.getId());
        budgetOneYearService.removeBatchByList(boyds);
        List<BudgetOneKpiDto> bokds = budgetOneKpiService.listByField("budgetOneId", bod.getId());
        budgetOneKpiService.removeBatchByList(bokds);
        List<BudgetOneFileDto> bofds = budgetOneFileService.listByField("budgetOneId", bod.getId());
        budgetOneFileService.removeBatchByList(bofds);
        // purchases
        if (CollUtil.isNotEmpty(vo.getPurchases())) {
            List<BudgetOnePurchaseDto> bopdList = new ArrayList<>();
            CollUtil.forEach(vo.getPurchases(), (CollUtil.Consumer<BudgetOnePurchaseVo>) (value, index) -> {
                BudgetOnePurchaseDto dto = BeanUtil.copyProperties(value, BudgetOnePurchaseDto.class);
                dto.setBudgetOneId(bod.getId());
                bopdList.add(dto);
            });
            budgetOnePurchaseService.saveBatch(bopdList);
        }
        // items
        if (CollUtil.isNotEmpty(vo.getItems())) {
            List<BudgetOneItemDto> boidList = new ArrayList<>();
            CollUtil.forEach(vo.getItems(), (CollUtil.Consumer<BudgetOneItemVo>) (value, index) -> {
                BudgetOneItemDto dto = BeanUtil.copyProperties(value, BudgetOneItemDto.class);
                dto.setBudgetOneId(bod.getId());
                boidList.add(dto);
            });
            budgetOneItemService.saveBatch(boidList);
        }
        // years
        if (CollUtil.isNotEmpty(vo.getYears())) {
            List<BudgetOneYearDto> boydList = new ArrayList<>();
            CollUtil.forEach(vo.getYears(), (CollUtil.Consumer<BudgetOneYearVo>) (value, index) -> {
                BudgetOneYearDto dto = BeanUtil.copyProperties(value, BudgetOneYearDto.class);
                dto.setBudgetOneId(bod.getId());
                boydList.add(dto);
            });
            budgetOneYearService.saveBatch(boydList);
        }
        // kpis
        List<BudgetOneKpiVo> kpis = vo.getKpis();
        if (CollUtil.isNotEmpty(kpis)) {
            CollUtil.forEach(kpis, (CollUtil.Consumer<BudgetOneKpiVo>) (one, i1) -> {
                if (one.getParentId() == null) {
                    BudgetOneKpiDto dto1 = BeanUtil.copyProperties(one, BudgetOneKpiDto.class);
                    dto1.setBudgetOneId(bod.getId());
                    budgetOneKpiService.save(dto1);
                    CollUtil.forEach(kpis, (CollUtil.Consumer<BudgetOneKpiVo>) (two, i2) -> {
                        if (two.getParentId() != null && two.getParentId().compareTo(one.getId()) == 0) {
                            BudgetOneKpiDto dto2 = BeanUtil.copyProperties(two, BudgetOneKpiDto.class);
                            dto2.setBudgetOneId(bod.getId());
                            dto2.setParentId(dto1.getId());
                            budgetOneKpiService.save(dto2);
                            CollUtil.forEach(kpis, (CollUtil.Consumer<BudgetOneKpiVo>) (three, i3) -> {
                                if (three.getParentId() != null && three.getParentId().compareTo(two.getId()) == 0) {
                                    BudgetOneKpiDto dto3 = BeanUtil.copyProperties(three, BudgetOneKpiDto.class);
                                    dto3.setBudgetOneId(bod.getId());
                                    dto3.setParentId(dto2.getId());
                                    if (StrUtil.isBlank(dto3.getKpiCode())) {
                                        BudgetKpiDto kpi = budgetKpiService.addBudgetKpi(KpiSource.budgetOne, dto3.getKpiName(), two.getId(), dto3.getWeight());
                                        if (kpi != null)
                                            dto3.setKpiCode(kpi.getCode());
                                    }
                                    budgetOneKpiService.save(dto3);
                                }
                            });
                        }
                    });
                }
            });
        }
        // files
        if (CollUtil.isNotEmpty(vo.getFiles())) {
            List<BudgetOneFileDto> boydList = new ArrayList<>();
            CollUtil.forEach(vo.getFiles(), (CollUtil.Consumer<BudgetOneFileVo>) (value, index) -> {
                BudgetOneFileDto dto = BeanUtil.copyProperties(value, BudgetOneFileDto.class);
                dto.setBudgetOneId(bod.getId());
                boydList.add(dto);
            });
            budgetOneFileService.saveBatch(boydList);
        }
        return bod;
    }

    @Transactional
    @Override
    public void submitBudgetOne(SubmitBudgetOneVo vo) {
        Assert.notNull(vo.getApproveId(), "请选择审批模版");
        SaveBudgetOneVo v = BeanUtil.copyProperties(vo, SaveBudgetOneVo.class);
        v.setState(BudgetState.submit.getKey());
        v.setItems(vo.getItems());
        v.setKpis(vo.getKpis());
        BudgetOneDto bod = this.saveBudgetOne(v);
        ApproveDto ad = approveService.getById(vo.getApproveId());
        approveOrderService.createApproveOrder(ad, BeanUtil.copyToList(vo.getApproveNodes(), ApproveOrderNodeDto.class), ApproveOrderBusinessType.budgetOne, bod.getId(), bod.getCode(), null, this.getClass());
        BudgetProjectTwoDto ptd = budgetProjectTwoService.getById(bod.getProjectTwoId());
        ptd.setLibraryState(BudgetProjectTwoLibraryState.construction.getKey());
        budgetProjectTwoService.updateById(ptd);
    }

    @Transactional
    @Override
    public void revocationBudgetOne(Long id) {
        BudgetOneDto dto = this.getById(id);
        Assert.notNull(dto, "找不到预算编制");
        dto.setState(BudgetState.revocation.getKey());
        this.updateById(dto);
        approveOrderService.revocationApproveOrder(ApproveOrderBusinessType.budgetOne, id);
    }

    @Override
    public List<BudgetOneDto> queryBudgetOneById(List<Long> idList) {
        List<BudgetOneDto> list = this.listByIds(idList);
        CollUtil.forEach(list, (CollUtil.Consumer<BudgetOneDto>) (value, index) -> calcBudgetOneTotalAmount(value));
        return list;
    }

    @Override
    public List<BudgetOneDto> queryBudgetOneByOrganizationId(Long organizationId) {
        //TODO
        return null;
    }

    @Override
    public List<BudgetOneDto> queryBudgetOneByProjectOneId(Long projectOneId) {
        List<BudgetOneDto> list = new ArrayList<>();
        List<BudgetProjectTwoDto> ptds = budgetProjectTwoService.listByField("projectOneId", projectOneId);
        CollUtil.forEach(ptds, (CollUtil.Consumer<BudgetProjectTwoDto>) (v1, i1) -> {
            List<BudgetOneDto> bods = listByField("projectTwoId", v1.getId());
            CollUtil.forEach(bods, (CollUtil.Consumer<BudgetOneDto>) (v2, i2) -> {
                calcBudgetOneTotalAmount(v2);
                list.add(v2);
            });
        });
        return list;
    }

    @Override
    public List<BudgetOneDto> queryBudgetOneByApplyGovernmentId(Long applyGovernmentId) {
        List<BudgetOneDto> list = new ArrayList<>();
        List<GovernmentDto> gds = governmentService.queryGovernmentAndSubs(applyGovernmentId);
        CollUtil.forEach(gds, (CollUtil.Consumer<GovernmentDto>) (v1, i1) -> {
            List<BudgetOneDto> bods = listByField("applyGovId", v1.getId());
            CollUtil.forEach(bods, (CollUtil.Consumer<BudgetOneDto>) (v2, i2) -> {
                calcBudgetOneTotalAmount(v2);
                list.add(v2);
            });
        });
        return list;
    }

    @Override
    public List<BudgetOneDto> queryBudgetOneByUseGovernmentId(Long useGovernmentId) {
        List<BudgetOneDto> list = new ArrayList<>();
        List<GovernmentDto> gds = governmentService.queryGovernmentAndSubs(useGovernmentId);
        CollUtil.forEach(gds, (CollUtil.Consumer<GovernmentDto>) (v1, i1) -> {
            List<BudgetOneDto> bods = listByField("useGovId", v1.getId());
            CollUtil.forEach(bods, (CollUtil.Consumer<BudgetOneDto>) (v2, i2) -> {
                calcBudgetOneTotalAmount(v2);
                list.add(v2);
            });
        });
        return list;
    }

    @Override
    public BigDecimal calcBudgetOneTotalAmount(BudgetOneDto dto) {
        if (dto == null) {
            return BigDecimal.ZERO;
        }
        AtomicReference<BigDecimal> all = new AtomicReference<>(BigDecimal.ZERO);
        List<BudgetOneItemDto> boids = budgetOneItemService.listByField("budgetOneId", dto.getId());
        CollUtil.forEach(boids, (CollUtil.Consumer<BudgetOneItemDto>) (boid, index) -> {
            BigDecimal sum = all.get().add(boid.getPrice().multiply(boid.getNum()));
            all.set(sum);
        });
        dto.setTotalAmount(all.get());
        return all.get();
    }

    @Transactional
    @Override
    public void adjustBudgetOneAmount(List<BudgetOneVo> bovs) {
        CollUtil.forEach(bovs, (CollUtil.Consumer<BudgetOneVo>) (v, index) -> {
            BudgetOneDto bod = getById(v.getId());
            bod.setApproveOneFund(v.getApproveOneFund());
            bod.setState(BudgetState.complete.getKey());
            BigDecimal applyOneFund = calcBudgetOneTotalAmount(bod);
            bod.setApplyOneFund(applyOneFund);
            updateById(bod);
            BudgetProjectTwoDto ptd = budgetProjectTwoService.getById(bod.getProjectTwoId());
            ptd.setState(BudgetProjectTwoState.budgetOneComplete.getKey());
            budgetProjectTwoService.updateById(ptd);
        });
    }

    @Transactional
    @Override
    public void callbackAfterApprove(Long businessId, boolean isPass, boolean isFinalPass) {
        if (businessId == null)
            return;
        if (isPass) {
            if (isFinalPass) {
                BudgetOneDto bod = this.getById(businessId);
                bod.setState(BudgetState.pass.getKey());
                this.updateById(bod);
                BudgetProjectTwoDto ptd = budgetProjectTwoService.getById(bod.getProjectTwoId());
                ptd.setState(BudgetProjectTwoState.budgetOneApproved.getKey());
                budgetProjectTwoService.updateById(ptd);
            }
        } else {
            BudgetOneDto bod = this.getById(businessId);
            bod.setState(BudgetState.reject.getKey());
            this.updateById(bod);
            BudgetProjectTwoDto ptd = budgetProjectTwoService.getById(bod.getProjectTwoId());
            ptd.setLibraryState(BudgetProjectTwoLibraryState.alternative.getKey());
            budgetProjectTwoService.updateById(ptd);
        }
    }

}
